package top.jfunc.token.mem;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 支持多设备登录
 * @author xiongshiyan at 2021/5/13 , contact me with email yanshixiong@126.com or phone 15208384257
 */
public abstract class BaseMemoryMultiTokenManager<M> extends MemoryTokenManager<M> {
    private static final Logger logger = LoggerFactory.getLogger(BaseMemoryMultiTokenManager.class);
    /**
     * 保存反向关系的map
     */
    private Map<Object,Object> map = new ConcurrentHashMap<>();
    /**
     * 多设备登录 ?允许多个token指向同一个人
     */
    private boolean multi = false;

    @Override
    public void updateExpires(String token) {
        if(null == token){
            return;
        }
        super.updateExpires(token);
        if(multi){
            return;
        }

        //多设备需要额外同步更新
        M m = findByToken(token);
        if(null != m){
            //redisTemplate.expire(key(m), apiExpires, TimeUnit.SECONDS);
        }
    }
    @Override
    public boolean deleteToken(String token) {
        //单设备需要额外删除
        M m = findByToken(token);

        boolean b = super.deleteToken(token);
        if(multi){
            return b;
        }

        String key = key(m);
        if(null != m){
            map.remove(key);
        }
        logger.info("deleteToken token = {} , key={}" , token , key);
        return b;
    }

    @Override
    public void saveToken(String token, M m) {
        super.saveToken(token, m);

        //单设备登录还需要额外保存实体和token的关系
        if(!multi){
            map.put(key(m), token);
        }
    }

    @Override
    public void kickingOld(M m, String newToken) {
        if(multi){
            throw new IllegalStateException("多设备登录情况不允许踢人");
        }
        //1.删除以前登录人的token，以前的人就通不过校验
        String key = key(m);
        String oldToken = (String) map.get(key);
        if(null != oldToken){
            super.deleteToken(oldToken);
        }

        //2.重新建立实体和新token的联系
        //单设备需要额外保存标识和token的关系
        logger.info("kickingOld key={} , value={}" , key , newToken);
        map.put(key, newToken);
    }

    /**
     * 根据实体或者标识获取key
     * @param m 实体或者标识
     * @return 返回保存标识和token关系的key
     */
    abstract protected String key(M m);

    public boolean isMulti() {
        return multi;
    }

    public void setMulti(boolean multi) {
        this.multi = multi;
    }
}
